#include "../header/bib_Planificadores.h"

extern struct stPLANIFICADOR *lsPlanif;
extern int cantListaPlanif;
extern sem_t accesoListaPlanif;

extern struct EST_CHS CHSPlanif;
extern char esperaCHS;
extern sem_t accesoCHS;

extern uint32_t proximoIdPlanif;
extern sem_t accesoIdPlanif;

//Prototipos
int determinarCHS(int sock);
struct stPLANIFICADOR *obtenerPlanifMenosCargado();
int eliminarEscrituraAntigua(struct stPLANIFICADOR *puntPlanif, uint32_t sector, int idListaPend);
int obtenerCantSolicPendientes(struct stPLANIFICADOR *planif);
void liberarEstructuraPlanif(uint8_t idPlanif);
void verificarSiTerminarProceso();
int enviarSolicitudAOtroPlanif(struct stSOLICITUD *solicitud);
struct stSOLICITUD *obtenerListaEscrituras();
int seDebeCopiar(struct stSOLICITUD *solicitud, struct stSOLICITUD *listaSolicitudes);

//Devuelve la cantidad de planificadores conectados
int obtenerCantidadPlanif(){
	int cant=0;

	//Solicitar semaforo
	sem_wait(&accesoListaPlanif);

		cant=cantListaPlanif;

	//Liberar semáforo
	sem_post(&accesoListaPlanif);

	return cant;
}

//Devuelve la cantidad de planificadores conectados y funcionales
int obtenerCantidadPlanifFunc(){
	int cant=0;
	uint32_t bloquesTotales;
	struct stPLANIFICADOR *puntPlanif;

	//Obtener la cantidad de sectores correctos
	bloquesTotales=cantSectoresPlanif(CHSPlanif)+1;

	//Solicitar semaforo
	sem_wait(&accesoListaPlanif);

	puntPlanif=lsPlanif;
	while(puntPlanif != NULL){
		if (puntPlanif->bloquesEspejados==bloquesTotales)
			cant++;

		puntPlanif=puntPlanif->sgte;
	}

	//Liberar semáforo
	sem_post(&accesoListaPlanif);

	return cant;
}

//Envia las solicitudes a los planificadores correspondientes
int distribuirSolicitud(struct HEADER_NIPC header, char *payload, int destino, int sock){
	int cantPlanif;
	struct stPLANIFICADOR *punteroPlanif=NULL;
	struct stSOLICITUD *solicitudAct;
	struct PAYLOAD_LECTURA operacionLect;
	struct PAYLOAD_ESCRITURA operacionEsc;

	struct PAYLOAD_RESPUESTA_LECTURA *respuestaDesdeCache;
	char textoAux[100];
	uint32_t bloquesTotales=cantSectoresPlanif(CHSPlanif)+1;

	//Actualizar lista de File System
	if (destino==DESTINO_FS && header.type==TIPO_ESCRITURA){
		//Indicar en la estructura del FS, la cantidad de escrituras que debe esperar
		cantPlanif=obtenerCantidadPlanifFunc();

		//No permitir que la cantidad de discos cambie hasta actualizar el valor
		sem_wait(&accesoListaPlanif);

		inicializarCantEsc(sock,cantPlanif);

		//Liberar semáforo
		sem_post(&accesoListaPlanif);
	}


	//Clasificar y distribuir a los planificadores
	if (header.type==TIPO_LECTURA){
		//Solicitud de lectura
		memcpy(&operacionLect,payload,sizeof(struct PAYLOAD_LECTURA));

		//Verificar si esta en la cache

		//Reservar memoria para los datos
		respuestaDesdeCache=(struct PAYLOAD_RESPUESTA_LECTURA*)malloc(sizeof (struct PAYLOAD_RESPUESTA_LECTURA));

		if (respuestaDesdeCache){

			if (leerDeCache(operacionLect.sector,respuestaDesdeCache->buffer)){
				//Se encuentra en cache, generar payload y responder al FS
				respuestaDesdeCache->id=operacionLect.id;
				header.payloadLength=sizeof(struct PAYLOAD_RESPUESTA_LECTURA);

				//Si corresponde, enviar respuesta al FS y quitarlo de la lista de solicitudes pendientes
				if (destino == DESTINO_FS){
					informarLecturaFS(sock, header, (char *)respuestaDesdeCache);

					free(respuestaDesdeCache);

					//Informar que se utilizo la cache
					sprintf(textoAux,"Se leyo de la cache el sector %u",operacionLect.sector);
					imprimirLog(CONSOLAFULL,textoAux,"");

					return FIN_OK;
				}else{
					//Enviar contenido al planificador correspondiente
					encolarEscrituraEspejo(&respuestaDesdeCache, destino, operacionLect.sector);

					free(respuestaDesdeCache);

					return FIN_OK;
				}
			}else
				free(respuestaDesdeCache);

		}

		sem_wait(&accesoListaPlanif);
		//Obtener el disco menos cargado
		punteroPlanif=obtenerPlanifMenosCargado();

		//Verificar si se encontro un planificador
		if (!punteroPlanif){
			//Error de memoria
			imprimirLog(ERROR, "No hay ningún planificador funcional","");

			responderErrorFS(sock, operacionLect.id);

			//Quitar de la lista de FS
			quitarFileSystemLista(sock);
			sem_post(&accesoListaPlanif);

			return FIN_WARNING;
		}


		solicitudAct=(struct stSOLICITUD *) malloc(sizeof(struct stSOLICITUD));
		if (!solicitudAct){
			//Error de memoria
			imprimirLog(WARNING, "Memoria insuficiente para solicitar una lectura a un planificador","");

			responderErrorFS(sock, operacionLect.id);

			//Quitar de la lista de FS
			quitarFileSystemLista(sock);
			sem_post(&accesoListaPlanif);

			return FIN_WARNING;
		}

		//Crear lectura y copiar el pedido
		solicitudAct->lectura=(struct PAYLOAD_LECTURA *) malloc(sizeof(struct PAYLOAD_LECTURA));
		memcpy(solicitudAct->lectura,payload,sizeof(struct PAYLOAD_LECTURA));

		//Inicializar solicitud
		solicitudAct->destino=destino;
		solicitudAct->escritura=NULL;
		solicitudAct->header=header;
		solicitudAct->socketFS=sock;

		//Colocar solicitud en la lista de pendientes, al principio por eficiencia
		sem_wait(&punteroPlanif->accesoPlanif);
		solicitudAct->sgte=punteroPlanif->solicitudPend;
		punteroPlanif->solicitudPend=solicitudAct;
		sem_post(&punteroPlanif->accesoPlanif);

		//Informar que se envio una solicitud de lectura al disco indicado
		sprintf(textoAux,"Se envio una solicitud de read del sector %u al planificador %u",solicitudAct->lectura->sector, punteroPlanif->idPlanificador);
		imprimirLog(CONSOLAFULL, textoAux,"");

		sem_post(&accesoListaPlanif);

	}else{
		//Solicitud de escritura
		memcpy(&operacionEsc,payload,sizeof(struct PAYLOAD_ESCRITURA));

		//Copiar en cache
		escribirEnCache(operacionEsc.sector,operacionEsc.buffer);

		sem_wait(&accesoListaPlanif);

		//Enviar la solicitud a todos los planificadores
		punteroPlanif=lsPlanif;
		while (punteroPlanif != NULL){
			//Crear una solicitud
			solicitudAct=(struct stSOLICITUD *) malloc(sizeof(struct stSOLICITUD));
			if (!solicitudAct){
				//Error de memoria
				imprimirLog(WARNING, "Memoria insuficiente para solicitar un escritura a los planificadores","");
				//Los planif. terminaran en estado incoherente, terminar proceso
				finalizarPRAID();
				sem_post(&accesoListaPlanif);
				return FIN_ERROR;
			}

			//Inicializar solicitud
			solicitudAct->destino=destino;
			solicitudAct->lectura=NULL;
			solicitudAct->header=header;
			solicitudAct->socketFS=sock;

			//Flag para no avisar que se ejecuto(Espejo)
			if (punteroPlanif->bloquesEspejados==bloquesTotales){
				solicitudAct->informarEjecucion=1;
			}else{
				solicitudAct->informarEjecucion=0;
			}

			//Copiar la operación a la solicitud
			solicitudAct->escritura=(struct PAYLOAD_ESCRITURA *) malloc(sizeof(struct PAYLOAD_ESCRITURA));
			if (!solicitudAct->escritura){
				//Error de memoria
				imprimirLog(WARNING, "Memoria insuficiente para solicitar un escritura a los planificadores","");
				//Los planif. terminaran en estado incoherente, terminar proceso
				finalizarPRAID();
				sem_post(&accesoListaPlanif);
				return FIN_ERROR;
			}
			//Convertir mensaje
			memcpy(solicitudAct->escritura,payload,sizeof(struct PAYLOAD_ESCRITURA));

			//Si hay otra escritura antigua pendiente en el mismo sector eliminarla
			sem_wait(&punteroPlanif->accesoPlanif);

			//Determinar si el disco se esta espejando
			if (punteroPlanif->bloquesEspejados < (cantSectoresPlanif(CHSPlanif)+1)){

				if (eliminarEscrituraAntigua(punteroPlanif, solicitudAct->escritura->sector,ESCRITURA_ESPEJO)){
					//Si habia una escritura en un sector a espejar, reemplazarlo
					//Colocar solicitud en la lista de pendientes para espejar, al principio por eficiencia
					solicitudAct->sgte=punteroPlanif->solicitudPendEspejar;
					punteroPlanif->solicitudPendEspejar=solicitudAct;
				}else{
					//Colocar en la cola de pendientes normal, al principio por eficiencia

					//Si ya existe una escritura, quitarla para reemplazarla
					eliminarEscrituraAntigua(punteroPlanif, solicitudAct->escritura->sector,ESCRITURA_NORMAL);

					solicitudAct->sgte=punteroPlanif->solicitudPend;
					punteroPlanif->solicitudPend=solicitudAct;
				}

			}else{
				//Disco funcional (Espejado)

				//Si ya existe una escritura, quitarla para reemplazarla
				eliminarEscrituraAntigua(punteroPlanif, solicitudAct->escritura->sector,ESCRITURA_NORMAL);

				//Colocar solicitud en la lista de pendientes, al principio por eficiencia
				solicitudAct->sgte=punteroPlanif->solicitudPend;
				punteroPlanif->solicitudPend=solicitudAct;
			}

			sem_post(&punteroPlanif->accesoPlanif);

			//Siguiente planificador
			punteroPlanif=punteroPlanif->sgte;
		}

		sem_post(&accesoListaPlanif);

		//Informar que se envio una solicitud de escritura a todos los discos
		sprintf(textoAux,"Se envio una solicitud de write del sector %u a todos los planificadores",solicitudAct->escritura->sector);
		imprimirLog(CONSOLAFULL, textoAux,"");
	}

	return FIN_OK;
}

//Agrega un nuevo planificador a la lista de planificadores
int agregarPlanificador(int sock, uint8_t idPlanif){
	struct stPLANIFICADOR *nuevoPlanif;
	struct stSOLICITUD *listaEscrituras=NULL;

	//Reservar memoria para la estructura
	nuevoPlanif=(struct stPLANIFICADOR *) malloc(sizeof(struct stPLANIFICADOR));
	if (!nuevoPlanif){
		//Error de memoria
		imprimirLogNumerico(WARNING, "Memoria insuficiente para aceptar un planificador",(int) idPlanif);
		cerrarSocket(sock);
		return FIN_WARNING;
	}

	//Inicializar estructura
	sem_init(&nuevoPlanif->accesoPlanif, 0, 1);
	sem_init(&nuevoPlanif->hiloFinalizado, 0, 0);
	nuevoPlanif->idPlanificador=idPlanif;
	nuevoPlanif->sockPlanif=sock;
	nuevoPlanif->solicitudPendEspejar=NULL;
	nuevoPlanif->solicitudEnv=NULL;
	nuevoPlanif->CHSCorrecto=0;	//Se desconoce si es correcto
	nuevoPlanif->terminarHilo=0;
	nuevoPlanif->bloquesEspejados=0;

	//Acceso exclusivo
	sem_wait(&accesoListaPlanif);

	//Añadir todas las escrituras de los otros procesos en la lista de pendientes, sin repetir
	//Para evitar incoherencias
	listaEscrituras=obtenerListaEscrituras();

	//Las escrituras nuevas se realizaran luego de espejar
	nuevoPlanif->solicitudPend=listaEscrituras;

	//Colocar el planificador al principio de la lista de planificadores
	nuevoPlanif->sgte=lsPlanif;
	lsPlanif=nuevoPlanif;
	cantListaPlanif++;

	sem_post(&accesoListaPlanif);
	
	//Crear hilo y pasar como parámetro el puntero de su propia estructura
	pthread_create((pthread_t *)&nuevoPlanif->hilo, NULL, (void *) &atencionPlanif, (void *) nuevoPlanif);

	return FIN_OK;
}

//Si no se conoce el CHS se solicita al planif. y se marca como pendiente
//Si se conoce solo se solicita el CHS para ver que se corresponda
//Si ya esta marcado como pendiente, se rechaza la conexion por no tener el planif. principal es estado funcional
int determinarCHS(int sock){
	struct HEADER_NIPC header;

	//Preparar header
	header.type=TIPO_SECTORES;
	header.payloadLength=0;

	sem_wait(&accesoCHS);

	//Determinar si el CHS es desconocido o conocido
	if (esperaCHS == 0){
		//Solicitar al planificador la cantidad de sectores

		//Enviar solicitud
		if (enviarMensajeNIPC(sock, &header, NULL)){
			//Error al enviar
			imprimirLog(WARNING,"El planificador se desconecto al solicitarle el CHS","");
			cerrarSocket(sock);
			sem_post(&accesoCHS);
			return FIN_WARNING;
		}

		if (CHSPlanif.sector == 0){
			//Colocar en espera
			esperaCHS=1;
		}
	}else{
		//Rechazar por no tener el disco principal en funcionamiento
		imprimirLog(WARNING,"Se rechazo la conexión del planificador por no tener el disco principal en funcionamiento","");
		cerrarSocket(sock);
		sem_post(&accesoCHS);
		return FIN_WARNING;
	}

	sem_post(&accesoCHS);
	return FIN_OK;
}

//Devuelve el ultimo sector lógico
uint32_t cantSectoresPlanif(struct EST_CHS chs){
	return (chs.sector * chs.cilindro - 1);
}

//Devuelve Verdadero si la id de planificador ya existe
int existeIdPlanif(uint8_t idPlanif){
	struct stPLANIFICADOR *puntero;

	//Solicitar semaforo
	sem_wait(&accesoIdPlanif);
	puntero=lsPlanif;

	while(puntero != NULL){
		if (puntero->idPlanificador==idPlanif){
			sem_post(&accesoIdPlanif);
			return RESP_TRUE;
		}

		puntero=puntero->sgte;
	}

	sem_post(&accesoIdPlanif);
	return RESP_FALSE;
}

//Obtiene un número de id de solicitud no utilizado
uint32_t generarIdSolicitud(){
	uint32_t valor;

	//Solicitar semaforo
	sem_wait(&accesoIdPlanif);

	valor=proximoIdPlanif;

	if (proximoIdPlanif==SOLICITUD_MAX)
		proximoIdPlanif=SOLICITUD_MIN;
	else
		proximoIdPlanif++;

	//Liberar semáforo
	sem_post(&accesoIdPlanif);

	return valor;
}

//Devuelve la estructura del disco menos cargado(Se supone que se tiene acceso exclusivo a lsPlanif)
struct stPLANIFICADOR *obtenerPlanifMenosCargado(){
	struct stPLANIFICADOR *punteroMinimo, *punteroAct;
	punteroMinimo=NULL;
	int cantMinima=-1;

	int auxCant;

	//Recorrer la lista
	punteroAct=lsPlanif;
	punteroMinimo=NULL;
	while(punteroAct != NULL){
		//Si esta en funcionamiento (CHS Correcto y todos los bloques espejados)
		if (punteroAct->CHSCorrecto && punteroAct->bloquesEspejados==(cantSectoresPlanif(CHSPlanif))+1){
			//Si el puntero minimo es Nulo, colocar al planif. actual como menos cargado
			if (punteroMinimo==NULL){
				punteroMinimo=punteroAct;
				cantMinima=obtenerCantSolicPendientes(punteroAct);
			}else{
				auxCant=obtenerCantSolicPendientes(punteroAct);
				if (auxCant<cantMinima){
					punteroMinimo=punteroAct;
					cantMinima=obtenerCantSolicPendientes(punteroAct);
				}
			}
		}

		//Siguiente puntero
		punteroAct=punteroAct->sgte;
	}

	//Devolver el planif con menos solicitudes
	return punteroMinimo;
}

struct stPLANIFICADOR *obtenerPuntPlanif(int sock){
	struct stPLANIFICADOR *punt;

	//Recorrer la lista
	punt=lsPlanif;

	while(punt != NULL){
		if (punt->sockPlanif==sock)
			return punt;

		punt=punt->sgte;
	}

	return punt;
}

//Devuelve la suma de la cantidad de solicitudes pendientes y enviadas
int obtenerCantSolicPendientes(struct stPLANIFICADOR *planif){
	int contador=0;
	struct stSOLICITUD *puntero=NULL;

	sem_wait(&planif->accesoPlanif);

	//Contar la cantidad de solicitudes pendientes
	puntero=planif->solicitudPend;
	while(puntero!=NULL){
		contador++;
		puntero=puntero->sgte;
	}

	//Contar la cantidad de solicitudes enviadas
	puntero=planif->solicitudEnv;
	while(puntero!=NULL){
		contador++;
		puntero=puntero->sgte;
	}

	//Liberar semáforo
	sem_post(&planif->accesoPlanif);

	return contador;
}

//Elimina la escritura al sector de la lista (Se tiene el acceso exclusivo a la estructura)
int eliminarEscrituraAntigua(struct stPLANIFICADOR *puntPlanif, uint32_t sector, int idListaPend){
	struct stSOLICITUD *ptrActual, *ptrAnterior;

	//Inicializar
	ptrAnterior = NULL;

	//Determinar la lista
	if (idListaPend==ESCRITURA_ESPEJO)
		ptrActual = puntPlanif->solicitudPendEspejar;
	else
		ptrActual = puntPlanif->solicitudPend;

	//Si no hay ninguna solicitud salir
	if (!(ptrActual))
		return RESP_FALSE;

	//Determinar si es el primero de la lista
	if (ptrActual->escritura != NULL){
		if(ptrActual->escritura->sector == sector){
			//Primero de la lista
			if (idListaPend==ESCRITURA_ESPEJO)
				puntPlanif->solicitudPendEspejar=ptrActual->sgte;
			else
				puntPlanif->solicitudPend=ptrActual->sgte;

			if (ptrActual->destino==DESTINO_FS){
				//Si la solicitud es para el file system, indicar que se ejecuto con exito
				informarEscrituraFS(ptrActual->socketFS, ptrActual->escritura->id);
			}

			//Liberar memoria
			free(ptrActual->escritura);
			free(ptrActual);
			return RESP_TRUE;
		}
	}else{
		//No hay ninguna solicitud pendiente
		return RESP_FALSE;
	}

	//Si no era el primero, recorrer la lista
	while(ptrActual != NULL){
		//Determinar si es de escritura
		if (ptrActual->escritura != NULL){
			if(ptrActual->escritura->sector == sector){

				//Quitar el puntero de la lista
				ptrAnterior->sgte=ptrActual->sgte;

				if (ptrActual->destino==DESTINO_FS){
					//Si la solicitud es para el file system, indicar que se ejecuto con exito
					informarEscrituraFS(ptrActual->socketFS, ptrActual->escritura->id);
				}

				//Liberar memoria
				free(ptrActual->escritura);
				free(ptrActual);
				return RESP_TRUE;
			}
		}

		ptrAnterior = ptrActual;
		ptrActual = ptrAnterior->sgte;

	}

	//No se encontro una escritura en el mismo sector
	return RESP_FALSE;
}

//Determina si corresponde el CHS, si es asi lo habilita
int compararCHS(int sock,struct EST_CHS respCHS){
	struct stPLANIFICADOR *punteroAct;
	char discoEspejado=0;

	//Verificar si el CHS es correcto
	sem_wait(&accesoCHS);

	//Verificar si es el primer planificador en conectarse
	if (CHSPlanif.sector == 0){
		//Es el primero en conectarse, copiar el CHS
		memcpy(&CHSPlanif, &respCHS, sizeof (struct EST_CHS));

		//Permitir que otros planificadores se conecten
		esperaCHS=0;

		//Indicar que se debe marcar como espejado
		discoEspejado=1;
	}else{

		//Comparar que el CHS sea correcto
		if (respCHS.sector != CHSPlanif.sector || respCHS.cilindro != CHSPlanif.cilindro || respCHS.cabezal != CHSPlanif.cabezal){
			sem_post(&accesoCHS);
			return FIN_WARNING;
		}
	}

	sem_post(&accesoCHS);

	sem_wait(&accesoListaPlanif);
	//Recorrer la lista, buscar el planificador y habilitar su funcionamiento
	punteroAct=lsPlanif;

	while(punteroAct != NULL){

		if (punteroAct->sockPlanif == sock){
			//Habilitar planificador
			sem_wait(&punteroAct->accesoPlanif);
			punteroAct->CHSCorrecto=1;
			if (discoEspejado){
				//Si esta espejado (Primer Planif.) indicarlo
				punteroAct->bloquesEspejados=cantSectoresPlanif(CHSPlanif)+1;
				//Informar por consola que el PRAID entro en funcionamiento
				imprimirLog(CONSOLA,"El proceso RAID entro en funcionamiento con el planificador","");
			}
			sem_post(&punteroAct->accesoPlanif);

			sem_post(&accesoListaPlanif);
			return FIN_OK;
		}

		//Siguiente puntero
		punteroAct=punteroAct->sgte;
	}

	//No se encontro el planificador
	sem_post(&accesoListaPlanif);
	return FIN_WARNING;
}

//Quita la estructura de la lista (Se necesita acceso exclusivo a la lista)
void liberarEstructuraPlanif(uint8_t idPlanif){
	struct stPLANIFICADOR *ptrActual, *ptrAnterior;

	ptrAnterior = NULL;
	ptrActual = lsPlanif;

	//Si no hay elementos en la lista salir
	if (lsPlanif==NULL){
		imprimirLogNumerico(WARNING,"Se solicito liberar la estructura de un planificador inexistente con identificador:",(int)idPlanif);
		return;
	}
	//Determinar si es el primero de la lista
	if (lsPlanif->idPlanificador == idPlanif){

		//Primero de la lista
		lsPlanif=ptrActual->sgte;
		free(ptrActual);
		cantListaPlanif--;

	}else{

		//Recorrer la lista
		while(ptrActual->idPlanificador != idPlanif && ptrActual != NULL){
			ptrAnterior = ptrActual;
			ptrActual = ptrAnterior->sgte;
		}

		//Liberar solicitud de memoria
		if(ptrActual != NULL){
			if (ptrActual->idPlanificador==idPlanif){
				ptrAnterior->sgte = ptrActual->sgte;
				free(ptrActual);
				cantListaPlanif--;
			}
		}
	}
}

//Devuelve el socket del FS que realizo la solicitud y lo elimina de la lista
//Si destino = DESTINO_FS, devuelve el socket del FS, sino destino indica el ID del planificador a enviar
int quitarSolicitudListaPlanif(uint8_t *destino, int *sockFS, int sockPlanif, uint32_t idSolicitud, uint32_t *sectorEspejar, char *informarEscritura){
	struct stSOLICITUD *ptrActual, *ptrAnterior;
	struct stPLANIFICADOR *ptrPlanif;
	uint32_t idRecuperado;

	//Buscar planificador
	sem_wait(&accesoListaPlanif);

	ptrPlanif=lsPlanif;

	while (ptrPlanif->sockPlanif != sockPlanif && ptrPlanif->sgte != NULL){
		ptrPlanif=ptrPlanif->sgte;
	}

	if (ptrPlanif->sockPlanif != sockPlanif){
		//No se encontro el planificador
		sem_post(&accesoListaPlanif);
		return FIN_WARNING;
	}

	//Recuperar la solicitud de la lista de enviados
	sem_wait(&ptrPlanif->accesoPlanif);

	ptrAnterior = NULL;
	ptrActual = ptrPlanif->solicitudEnv;

	//Si no hay elementos en la lista salir
	if (ptrActual==NULL){
		//No se encontro la solicitud
		sem_post(&ptrPlanif->accesoPlanif);
		sem_post(&accesoListaPlanif);
		return FIN_WARNING;
	}

	//Determinar si es el primero de la lista
	if (ptrActual->lectura != NULL)
		idRecuperado=ptrActual->lectura->id;
	else
		idRecuperado=ptrActual->escritura->id;

	if (idRecuperado == idSolicitud){
		//Primero de la lista, quitarlo de la lista
		ptrPlanif->solicitudEnv=ptrActual->sgte;

	}else{

		//Recorrer la lista
		while(idRecuperado != idSolicitud && ptrActual != NULL){
			ptrAnterior = ptrActual;
			ptrActual = ptrAnterior->sgte;

			//Recuperar idSolicitud
			if (ptrActual->lectura != NULL)
				idRecuperado=ptrActual->lectura->id;
			else
				idRecuperado=ptrActual->escritura->id;
		}

		//Verificar
		if(ptrActual != NULL){
			//Encontrado
			ptrAnterior->sgte = ptrActual->sgte;
		}else{
			//No se encontro la solicitud
			sem_post(&ptrPlanif->accesoPlanif);
			sem_post(&accesoListaPlanif);
			return FIN_WARNING;
		}
	}

	//Obtener datos de la solicitud
	*destino=ptrActual->destino;
	*informarEscritura=ptrActual->informarEjecucion;

	*sockFS=ptrActual->socketFS;
	if (ptrActual->lectura != NULL)
		*sectorEspejar=ptrActual->lectura->sector;

	//Liberar memoria
	if (ptrActual->escritura != NULL)
		free(ptrActual->escritura);
	else
		free(ptrActual->lectura);
	free(ptrActual);

	//Liberar semaforos
	sem_post(&ptrPlanif->accesoPlanif);
	sem_post(&accesoListaPlanif);

	return FIN_OK;
}

//Coloca una solicitud de escritura en la cola de pendientes para espejar
int encolarEscrituraEspejo(struct PAYLOAD_RESPUESTA_LECTURA *contenido, uint8_t idPlanif, uint32_t sector){
	struct stPLANIFICADOR *ptrPlanif;
	struct stSOLICITUD *nuevaSolicitud;

	//Reservar memoria
	nuevaSolicitud=(struct stSOLICITUD *) malloc(sizeof(struct stSOLICITUD));
	if (!nuevaSolicitud){
		imprimirLogNumerico(ERROR, "Memoria insuficiente para crear una solicitud para espejar un sector para el planificador",idPlanif);
		return FIN_ERROR;
	}

	//Crear solicitud para escritura
	nuevaSolicitud->header.type=TIPO_ESCRITURA;
	nuevaSolicitud->header.payloadLength=sizeof(struct PAYLOAD_ESCRITURA);
	nuevaSolicitud->lectura=NULL;
	nuevaSolicitud->socketFS=0;
	nuevaSolicitud->destino=idPlanif;

	//Reservar memoria
	nuevaSolicitud->escritura=(struct PAYLOAD_ESCRITURA *) malloc(sizeof(struct PAYLOAD_ESCRITURA));
	if (!nuevaSolicitud->escritura){
		free(nuevaSolicitud);
		imprimirLogNumerico(ERROR, "Memoria insuficiente para crear una solicitud para espejar un sector para el planificador",idPlanif);
		return FIN_ERROR;
	}

	memcpy(nuevaSolicitud->escritura->buffer,contenido->buffer,sizeof(char)*TAM_SECTOR);
	nuevaSolicitud->escritura->id=generarIdSolicitud();
	nuevaSolicitud->escritura->sector=sector;

	//Buscar planificador
	sem_wait(&accesoListaPlanif);

	ptrPlanif=lsPlanif;
	while (ptrPlanif->idPlanificador != idPlanif && ptrPlanif->sgte != NULL){
		ptrPlanif=ptrPlanif->sgte;
	}

	if (ptrPlanif->idPlanificador != idPlanif){
		//No se encontro el planificador
		sem_post(&accesoListaPlanif);

		free(nuevaSolicitud->escritura);
		free(nuevaSolicitud);

		return FIN_WARNING;
	}

	sem_wait(&ptrPlanif->accesoPlanif);

	//Colocar al principio de la cola para espejar (Por eficiencia)
	nuevaSolicitud->sgte=ptrPlanif->solicitudPendEspejar;
	ptrPlanif->solicitudPendEspejar=nuevaSolicitud;

	sem_post(&ptrPlanif->accesoPlanif);
	sem_post(&accesoListaPlanif);

	return FIN_OK;
}

//Incrementa en uno los sectores espejados del planificador especificado
int calcularValorEspejado(int sockPlanif){
	struct stPLANIFICADOR *ptrPlanif;

	//Buscar planificador
	sem_wait(&accesoListaPlanif);

	ptrPlanif=lsPlanif;
	while (ptrPlanif->sockPlanif != sockPlanif && ptrPlanif->sgte != NULL){
		ptrPlanif=ptrPlanif->sgte;
	}

	if (ptrPlanif->sockPlanif != sockPlanif){
		//No se encontro el planificador
		sem_post(&accesoListaPlanif);
		return FIN_WARNING;
	}

	sem_wait(&ptrPlanif->accesoPlanif);

	//Incrementar en 1 los sectores espejados
	ptrPlanif->bloquesEspejados=ptrPlanif->bloquesEspejados+1;

	sem_post(&ptrPlanif->accesoPlanif);
	sem_post(&accesoListaPlanif);

	return FIN_OK;
}

//Incrementa en uno los sectores espejados del planificador especificado
int calcularValorEspejadoPunt(struct stPLANIFICADOR *planif){
	//Si no hay planificadores salir
	if (planif==NULL){
		imprimirLog(ERROR,"Se necesita actualizar el valor de espejado de un planificador inexistente","");
		return FIN_ERROR;
	}

	//Buscar planificador
	sem_wait(&accesoListaPlanif);
	sem_wait(&planif->accesoPlanif);

	//Incrementar en 1 los sectores espejados
	planif->bloquesEspejados=planif->bloquesEspejados+1;

	sem_post(&planif->accesoPlanif);
	sem_post(&accesoListaPlanif);

	return FIN_OK;
}

//Busca la estructura, la libera y distribuye su carga
int liberarYDistribuirCarga(int sock){
	struct stPLANIFICADOR *ptrPlanif;
	struct stSOLICITUD *prtSolic;

	struct stSOLICITUD *ptrSolicExtraida;

	//Buscar planificador
	sem_wait(&accesoListaPlanif);

	ptrPlanif=lsPlanif;
	while (ptrPlanif->sockPlanif != sock && ptrPlanif->sgte != NULL){
		ptrPlanif=ptrPlanif->sgte;
	}

	if (ptrPlanif->sockPlanif != sock){
		//No se encontro el planificador
		sem_post(&accesoListaPlanif);
		return FIN_WARNING;
	}

	//Informar por log que el planificador se desconecto
	imprimirLogNumerico(CONSOLA, "Se desconecto el planificador",ptrPlanif->idPlanificador);

	//Inhabilitar planificador actual
	ptrPlanif->CHSCorrecto=0;

	//Finalizar el hilo
	sem_post(&accesoListaPlanif);

	ptrPlanif->terminarHilo=1;
	sem_wait(&ptrPlanif->hiloFinalizado);

	sem_wait(&accesoListaPlanif);

	sem_wait(&ptrPlanif->accesoPlanif);

	//Recorrer la lista de pendientes para espejar
	while (ptrPlanif->solicitudPendEspejar != NULL){
		if (ptrPlanif->solicitudPendEspejar->destino == DESTINO_FS){
			//Indicar al File System que se realizo la escritura correctamente
			informarEscrituraFS(ptrPlanif->solicitudPendEspejar->socketFS, ptrPlanif->solicitudPendEspejar->escritura->id);
		}

		//Quitar solicitud de la lista
		prtSolic=ptrPlanif->solicitudPendEspejar;
		if (prtSolic != NULL)
			ptrPlanif->solicitudPendEspejar=prtSolic->sgte;

		//Liberar recursos
		free(prtSolic->escritura);
		free(prtSolic);
	}

	//Recorrer la lista de pendientes normal
	while (ptrPlanif->solicitudPend != NULL){

		//Determinar si es de escritura
		if (ptrPlanif->solicitudPend->escritura != NULL){

			if (ptrPlanif->solicitudPend->destino == DESTINO_FS){
				//Indicar al File System que se realizo la escritura correctamente
				informarEscrituraFS(ptrPlanif->solicitudPend->socketFS, ptrPlanif->solicitudPend->escritura->id);
			}

			//Quitar solicitud de la lista
			prtSolic=ptrPlanif->solicitudPend;
			ptrPlanif->solicitudPend=prtSolic->sgte;

			//Liberar recursos
			free(prtSolic->escritura);
			free(prtSolic);
		}else{

			//Solicitud de lectura

			//Extraer la primera solicitud de la lista
			ptrSolicExtraida=ptrPlanif->solicitudPend;
			ptrPlanif->solicitudPend=ptrSolicExtraida->sgte;

			//Enviar a otro planificador libre
			if (enviarSolicitudAOtroPlanif(ptrSolicExtraida)){
				//No hay planificador disponible

				//Liberar recursos
				free(ptrSolicExtraida->lectura);
				free(ptrSolicExtraida);
			}
		}
	}

	//Recorrer la lista de enviados
	while (ptrPlanif->solicitudEnv != NULL){

		//Determinar si es de escritura
		if (ptrPlanif->solicitudEnv->escritura != NULL){

			if (ptrPlanif->solicitudEnv->destino == DESTINO_FS){
				//Indicar al File System que se realizo la escritura correctamente
				informarEscrituraFS(ptrPlanif->solicitudEnv->socketFS, ptrPlanif->solicitudEnv->escritura->id);
			}

			//Quitar solicitud de la lista
			prtSolic=ptrPlanif->solicitudEnv;
			ptrPlanif->solicitudEnv=prtSolic->sgte;

			//Liberar recursos
			free(prtSolic->escritura);
			free(prtSolic);
		}else{

			//Solicitud de lectura

			//Extraer la primera solicitud de la lista
			ptrSolicExtraida=ptrPlanif->solicitudEnv;
			ptrPlanif->solicitudEnv=ptrSolicExtraida->sgte;

			//Enviar a otro planificador libre
			if (enviarSolicitudAOtroPlanif(ptrSolicExtraida)){
				//No hay planificador disponible

				//Liberar recursos
				free(ptrSolicExtraida->lectura);
				free(ptrSolicExtraida);
			}
		}
	}

	sem_post(&ptrPlanif->accesoPlanif);

	//Liberar estructura
	liberarEstructuraPlanif(ptrPlanif->idPlanificador);

	sem_post(&accesoListaPlanif);

	//Verificar si se puede continuar el proceso sin el planificador
	verificarSiTerminarProceso();

	return FIN_OK;
}

//Verifica si hay planificadores suficientes para continuar
void verificarSiTerminarProceso(){
	struct stPLANIFICADOR *puntero;

	sem_wait(&accesoListaPlanif);

	//Si no hay ningún planificador, terminar
	if (cantListaPlanif==0){
		//Terminar
		finalizarPRAID();
	}else{

		//Recorrer la lista de planificadores
		puntero=lsPlanif;
		while (puntero != NULL){
			if (puntero->CHSCorrecto && puntero->bloquesEspejados==cantSectoresPlanif(CHSPlanif)+1){
				//Hay por lo menos un planificador funcional
				sem_post(&accesoListaPlanif);
				return;
			}

			//Siguiente planificador
			puntero=puntero->sgte;
		}

		//No hay ningun planificador funcional
		finalizarPRAID();
	}

	sem_post(&accesoListaPlanif);
}

//Envia una solicitud de lectura al planificador con menos carga
//(Se necesita acceso exclusivo a la lista de planificadores y a su estructura)
int enviarSolicitudAOtroPlanif(struct stSOLICITUD *solicitud){
	struct stPLANIFICADOR *punteroPlanif=NULL;

	//Obtener el disco menos cargado
	punteroPlanif=obtenerPlanifMenosCargado();

	//Si no hay, liberar solicitud y salir (El praid no podra continuar sin planificador funcional)
	if (punteroPlanif == NULL){
		return FIN_WARNING;
	}

	//Colocar en la lista de pendientes
	sem_wait(&punteroPlanif->accesoPlanif);
	solicitud->sgte = punteroPlanif->solicitudPend;
	punteroPlanif->solicitudPend = solicitud;
	sem_post(&punteroPlanif->accesoPlanif);

	return FIN_OK;
}

//Obtene la lista de todas las escrituras pendientes de los planificadores, sin repetir
//Solo las escrituras provenientes del File System
//(Se necesita acceso exclusivo a la lista de planificadores)
struct stSOLICITUD *obtenerListaEscrituras(){
	struct stSOLICITUD *copiaSolicitud;
	struct stPLANIFICADOR *puntPlanif;
	struct stSOLICITUD *puntSolic;
	char idLista; 	//0: Pend. Espejar, 1: Pend. Normal, 2: Enviados

	struct stSOLICITUD *listaEscrituras;

	//Inicializar
	listaEscrituras=NULL;

	//Recorrer la lista de planificadores
	puntPlanif=lsPlanif;
	while(puntPlanif != NULL){

		//Semaforo de la estructura
		sem_wait(&puntPlanif->accesoPlanif);

		//Buscar solo en los planificadores funcionales
		if (puntPlanif->CHSCorrecto && puntPlanif->bloquesEspejados==(cantSectoresPlanif(CHSPlanif))+1){

			//Recorrer las 3 listas
			for (idLista=0; idLista <=2; idLista++){

				//Determinar la lista a recorrer
				switch (idLista) {
					case 0:
						puntSolic=puntPlanif->solicitudPendEspejar;
						break;
					case 1:
						puntSolic=puntPlanif->solicitudPend;
						break;
					case 2:
						puntSolic=puntPlanif->solicitudEnv;
						break;
				}

				//Recorrer la lista
				while(puntSolic != NULL){

					if (seDebeCopiar(puntSolic,listaEscrituras)){
						//Se debe copiar a la lista

						//Reservar memoria para la copia
						copiaSolicitud=(struct stSOLICITUD *) malloc(sizeof(struct stSOLICITUD));
						if (!copiaSolicitud){
							//Error de memoria
							imprimirLog(WARNING, "Memoria insuficiente para espejar un planificador","");
							sem_post(&puntPlanif->accesoPlanif);
							return listaEscrituras;
						}

						//Copiar contenido
						memcpy(copiaSolicitud, puntSolic, sizeof(struct stSOLICITUD));

						//Reservar memoria para el campo de escritura
						copiaSolicitud->escritura=(struct PAYLOAD_ESCRITURA *) malloc(sizeof(struct PAYLOAD_ESCRITURA));
						if (!copiaSolicitud->escritura){
							//Error de memoria
							imprimirLog(WARNING, "Memoria insuficiente para espejar un planificador","");
							free(copiaSolicitud);
							sem_post(&puntPlanif->accesoPlanif);
							return listaEscrituras;
						}

						//Copiar contenido de escritura
						memcpy(copiaSolicitud->escritura, puntSolic->escritura, sizeof(struct PAYLOAD_ESCRITURA));

						//Modificar campos
						copiaSolicitud->destino=DESTINO_FS+1;	//Al ser distinto de DESTINO_FS, se toma como para espejar
						copiaSolicitud->socketFS=0;
						copiaSolicitud->escritura->id = generarIdSolicitud();
						copiaSolicitud->informarEjecucion=0;

						//Colocar al principio de la lista, por eficiencia
						copiaSolicitud->sgte= listaEscrituras;
						listaEscrituras=copiaSolicitud;
					}

					//Siguiente de la lista
					puntSolic=puntSolic->sgte;
				}
			}
		}

		//Liberar semaforo de la estructura
		sem_post(&puntPlanif->accesoPlanif);

		//Siguiente planificador
		puntPlanif=puntPlanif->sgte;
	}

	return listaEscrituras;
}

//Verifica que la solicitud sea del File System y no este en la lista de solicitudes
//(Necesita acceso exclusivo a la lista de planificadores y al planificador al que pertenece)
int seDebeCopiar(struct stSOLICITUD *solicitud, struct stSOLICITUD *listaSolicitudes){
	uint32_t sector;
	struct stSOLICITUD *puntSolic;

	//Si es una solicitud de lectura o para espejar se rechaza
	if (solicitud->destino != DESTINO_FS || solicitud->escritura == NULL){
		return RESP_FALSE;
	}

	//Verificar que no se repita en la lista
	sector=solicitud->escritura->sector;
	puntSolic=listaSolicitudes;
	while (puntSolic != NULL){

		//Verificar que sea de escritura
		if (puntSolic->escritura != NULL){
			if (puntSolic->escritura->sector == sector){
				//Ya esta la solicitud en el mismo sector, rechazar
				return RESP_FALSE;
			}
		}

		puntSolic=puntSolic->sgte;
	}

	//No esta la escritura hacia ese sector, aceptar
	return RESP_TRUE;
}

//Devuelve la cantidad de sectores espejados
//(Se necesita acceso a la lista de planif)
uint32_t obtenerValorEspejados(struct stPLANIFICADOR *planif){
	uint32_t valor;

	sem_wait(&planif->accesoPlanif);
	valor=planif->bloquesEspejados;
	sem_post(&planif->accesoPlanif);

	return valor;
}

//Devuelve si un planificador esta funcional (Todos los sectores espejados)
//(Se necesita acceso a la lista de planif)
int planificadorEspejado(struct stPLANIFICADOR *planif){
	uint32_t valorEspejado, valorEspejadoOK;

	valorEspejado=obtenerValorEspejados(planif);
	valorEspejadoOK=cantSectoresPlanif(CHSPlanif);

	if (valorEspejado==(valorEspejadoOK+1)){
		return RESP_TRUE;
	}

	return RESP_FALSE;
}
